//-
// ==========================================================================
// Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors.  All 
// rights reserved.
//
// The coded instructions, statements, computer programs, and/or related 
// material (collectively the "Data") in these files contain unpublished 
// information proprietary to Autodesk, Inc. ("Autodesk") and/or its 
// licensors, which is protected by U.S. and Canadian federal copyright 
// law and by international treaties.
//
// The Data is provided for use exclusively by You. You have the right 
// to use, modify, and incorporate this Data into other products for 
// purposes authorized by the Autodesk software license agreement, 
// without fee.
//
// The copyright notices in the Software and this entire statement, 
// including the above license grant, this restriction and the 
// following disclaimer, must be included in all copies of the 
// Software, in whole or in part, and all derivative works of 
// the Software, unless such copies or derivative works are solely 
// in the form of machine-executable object code generated by a 
// source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. 
// AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED 
// WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF 
// NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR 
// PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR 
// TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS 
// BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL, 
// DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK 
// AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY 
// OR PROBABILITY OF SUCH DAMAGES.
//
// ==========================================================================
//+

//
//	File Name:	animImport.cc
//
//	Description:
//		Imports a .anim file into anim curves attached to the selected
//		Maya objects.
//

#include <stdlib.h>
#include <string.h>

#include <maya/MFStream.h>
#include <maya/MGlobal.h>
#include <maya/MString.h>
#include <maya/MFnPlugin.h>
#include <maya/MPxFileTranslator.h>

#include <maya/MSelectionList.h>

#include <maya/MFnAnimCurve.h>
#include <maya/MAnimCurveClipboard.h>
#include <maya/MAnimCurveClipboardItem.h>
#include <maya/MAnimCurveClipboardItemArray.h>

#include "animImportExport.h"
#include "animFileUtils.h"
#include "animImportExportStrings.h"

#if defined (OSMac_)
#	include <sys/param.h>
extern "C" int strcasecmp (const char *, const char *);
#endif
//-----------------------------------------------------------------------------
//	anim Importer
//-----------------------------------------------------------------------------

const char *const animImportOptionScript = "animImportOptions";
const char *const animImportDefaultOptions = 
	"targetTime=4;copies=1;option=replace;pictures=0;connect=0;";

// Register all strings used by the plugin C++ code
static MStatus registerMStringResources(void)
{
	MStringResource::registerString(kNothingSelected);
	MStringResource::registerString(kPasteFailed);
	MStringResource::registerString(kAnimCurveNotFound);		
	MStringResource::registerString(kInvalidAngleUnits);
	MStringResource::registerString(kInvalidLinearUnits);
	MStringResource::registerString(kInvalidTimeUnits);
	MStringResource::registerString(kInvalidVersion);
	MStringResource::registerString(kSettingToUnit);
	MStringResource::registerString(kMissingKeyword);
	MStringResource::registerString(kCouldNotReadAnim);
	MStringResource::registerString(kCouldNotCreateAnim);	
	MStringResource::registerString(kUnknownKeyword);
	MStringResource::registerString(kClipboardFailure);
	MStringResource::registerString(kSettingTanAngleUnit);
	MStringResource::registerString(kUnknownNode);
	MStringResource::registerString(kCouldNotKey);
	MStringResource::registerString(kMissingBrace);
	MStringResource::registerString(kCouldNotExport);
	return MS::kSuccess;
}



animImport::animImport()
: MPxFileTranslator()
{
}

animImport::~animImport()
{
}

void *animImport::creator()
{
	return new animImport();
}

MStatus animImport::reader(	const MFileObject& file,
								const MString& options,
								FileAccessMode mode)
{
	MStatus status = MS::kFailure;

	MString fileName = file.fullName();
#if defined (OSMac_)	
	char fname[MAXPATHLEN];
	strcpy (fname, fileName.asChar());
	ifstream animFile(fname);
#else
	ifstream animFile(fileName.asChar());
#endif
	// 	Parse the options. The options syntax is in the form of
	//	"flag=val;flag1=val;flag2=val"
	//
	MString pasteFlags;
	if (options.length() > 0) {
		//	Set up the flags for the paste command.
		//
		const MString flagTargetTime("targetTime");
		const MString flagTime("time");
		const MString flagCopies("copies");
		const MString flagOption("option");
		const MString flagConnect("connect");

		MString copyValue;
		MString flagValue;
		MString connectValue;
		MString timeValue;

		//	Start parsing.
		//
		MStringArray optionList;
		MStringArray theOption;
		options.split(';', optionList);

		unsigned nOptions = optionList.length();
		for (unsigned i = 0; i < nOptions; i++) {

			theOption.clear();
			optionList[i].split('=', theOption);
			if (theOption.length() < 1) {
				continue;
			}

			if (theOption[0] == flagCopies && theOption.length() > 1) {
				copyValue = theOption[1];;
			} else if (theOption[0] == flagOption && theOption.length() > 1) {
				flagValue = theOption[1];
			} else if (theOption[0] == flagConnect && theOption.length() > 1) {
				if (theOption[1].asInt() != 0) {
					connectValue += theOption[1];
				}
			} else if (theOption[0] == flagTime && theOption.length() > 1) {
				timeValue += theOption[1];
			} 
		}
	
		if (copyValue.length() > 0) {
			pasteFlags += " -copies ";
			pasteFlags += copyValue;
			pasteFlags += " ";
		} 
		if (flagValue.length() > 0) {
			pasteFlags += " -option \"";
			pasteFlags += flagValue;
			pasteFlags += "\" ";
		} 
		if (connectValue.length() > 0) {
			pasteFlags += " -connect ";
			pasteFlags += connectValue;
			pasteFlags += " ";
		} 
		if (timeValue.length() > 0) {
			bool useQuotes = !timeValue.isDouble();
			pasteFlags += " -time ";
			if (useQuotes) pasteFlags += "\"";
			pasteFlags += timeValue;
			if (useQuotes) pasteFlags += "\"";
			pasteFlags += " ";
		} 
	}

	if (mode == kImportAccessMode) {
		status = importAnim(animFile, pasteFlags);
	}

	animFile.close();
	return status;
}

bool animImport::haveReadMethod() const
{
	return true;
}

bool animImport::haveWriteMethod() const
{
	return false;
}

bool animImport::canBeOpened() const
{
	return false;
}

MString animImport::defaultExtension() const
{
	return MString("anim");
}

MPxFileTranslator::MFileKind animImport::identifyFile(
								const MFileObject& fileName,
								const char* buffer,
								short size) const
{
	const char *name = fileName.name().asChar();
	int   nameLength = (int)strlen(name);

	if ((nameLength > 5) && !strcasecmp(name+nameLength-5, ".anim")) {
		return kIsMyFileType;
	}

	//	Check the buffer to see if this contains the correct keywords
	//	to be a anim file.
	//
	if (strncmp(buffer, "animVersion", 11) == 0) {
		return kIsMyFileType;
	}

	return	kNotMyFileType;
}

MStatus 
animImport::importAnim(ifstream &animFile, const MString &pasteFlags)
{
	MStatus status = MS::kFailure;
	MAnimCurveClipboard::theAPIClipboard().clear();

	//	If the selection list is empty, there is nothing to import.
	//
	MSelectionList sList;
	MGlobal::getActiveSelectionList(sList);
	if (sList.isEmpty()) {
		MString msg = MStringResource::getString(kNothingSelected, status);
		MGlobal::displayError(msg);
		return (MS::kFailure);
	}

	if (MS::kSuccess != 
			(status = fReader.readClipboard(animFile, 
			MAnimCurveClipboard::theAPIClipboard()))) {

		return status;
	}

	if (MAnimCurveClipboard::theAPIClipboard().isEmpty()) {
		return (MS::kFailure);
	}

	MString command("pasteKey -cb api ");
	command += pasteFlags;

	int result;
	if (MS::kSuccess != (status =  
		MGlobal::executeCommand(command, result, false, true))) {
		MString msg = MStringResource::getString(kPasteFailed, status);
		MGlobal::displayError(msg);
		return status;
	}

	return status;
}

//-----------------------------------------------------------------------------
//	anim Exporter
//-----------------------------------------------------------------------------

const char *const animExportOptionScript = "animExportOptions";
const char *const animExportDefaultOptions = "precision=8;nodeNames=1;verboseUnits=0;whichRange=1;range=0:10;options=keys;hierarchy=none;controlPoints=0;shapes=1;helpPictures=0;useChannelBox=0;copyKeyCmd=";

const int kDefaultPrecision = 8;	//	float precision.

animExport::animExport()
: MPxFileTranslator()
{
}

animExport::~animExport()
{
}

void *animExport::creator()
{
	return new animExport();
}

MStatus animExport::writer(	const MFileObject& file,
								const MString& options,
								FileAccessMode mode)
{
	MStatus status = MS::kFailure;

#ifdef MAYA_EVAL_VERSION
	status = MS::kFailure;
	return status;
#endif

	MString fileName = file.fullName();
#if defined (OSMac_)
	char fname[MAXPATHLEN];
	strcpy (fname, fileName.asChar());
	ofstream animFile(fname);
#else
	ofstream animFile(fileName.asChar());
#endif
	//	Defaults.
	//
	MString copyFlags("copyKey -cb api -fea 1 ");
	int precision = kDefaultPrecision;
	bool nodeNames = true;
	bool verboseUnits = false;

	//	Parse the options. The options syntax is in the form of
	//	"flag=val;flag1=val;flag2=val"
	//
	MString exportFlags;
	if (options.length() > 0) {
		const MString flagPrecision("precision");
		const MString flagNodeNames("nodeNames");
		const MString flagVerboseUnits("verboseUnits");
		const MString flagCopyKeyCmd("copyKeyCmd");

		//	Start parsing.
		//
		MStringArray optionList;
		MStringArray theOption;
		options.split(';', optionList);

		unsigned nOptions = optionList.length();
		for (unsigned i = 0; i < nOptions; i++) {
			theOption.clear();
			optionList[i].split('=', theOption);
			if (theOption.length() < 1) {
				continue;
			}

			if (theOption[0] == flagPrecision && theOption.length() > 1) {
				if (theOption[1].isInt()) {
					precision = theOption[1].asInt();
				}
			} else if (	theOption[0] == 
						flagNodeNames && theOption.length() > 1) {
				if (theOption[1].isInt()) {
					nodeNames = (theOption[1].asInt()) ? true : false;
				}
			}
			else if (theOption[0] == 
					flagVerboseUnits && theOption.length() > 1) {
				if (theOption[1].isInt()) {
					verboseUnits = (theOption[1].asInt()) ? true : false;
				}
			} else if (	theOption[0] == 
						flagCopyKeyCmd && theOption.length() > 1) {

				//	Replace any '>' characters with '"'. This is needed
				//	since the file translator option boxes do not handle
				//	escaped quotation marks.
				//
				const char *optStr = theOption[1].asChar();
				size_t nChars = strlen(optStr);
				char *copyStr = new char[nChars+1];

				copyStr = strcpy(copyStr, optStr);
				for (size_t j = 0; j < nChars; j++) {
					if (copyStr[j] == '>') {
						copyStr[j] = '"';
					}
				}
		
				copyFlags += copyStr;
				delete copyStr;
			}
		}
	}
	
	//	Set the precision of the ofstream.
	//
	animFile.precision(precision);

	status = exportSelected(animFile, copyFlags, nodeNames, verboseUnits);

	animFile.flush();
	animFile.close();

	return status;
}

bool animExport::haveReadMethod() const
{
	return false;
}

bool animExport::haveWriteMethod() const
{
#ifdef MAYA_EVAL_VERSION
	return false;
#else
	return true;
#endif
}

MString animExport::defaultExtension() const
{
	return MString("anim");
}

MPxFileTranslator::MFileKind animExport::identifyFile(
								const MFileObject& fileName,
								const char* buffer,
								short size) const
{
	const char *name = fileName.name().asChar();
	int   nameLength = (int)strlen(name);

	if ((nameLength > 5) && !strcasecmp(name+nameLength-5, ".anim")) {
		return kIsMyFileType;
	}

	return	kNotMyFileType;
}

MStatus animExport::exportSelected(	ofstream &animFile, 
									MString &copyFlags,
									bool nodeNames /* false */,
									bool verboseUnits /* false */)
{
	MStatus status = MS::kFailure;

	//	If the selection list is empty, then there are no anim curves
	//	to export.
	//
	MSelectionList sList;
	MGlobal::getActiveSelectionList(sList);
	if (sList.isEmpty()) {
		MString msg = MStringResource::getString(kNothingSelected, status);
		MGlobal::displayError(msg);
		return (MS::kFailure);
	}

	//	Copy any anim curves to the API clipboard.
	//
	int result = 0;
	MString command(copyFlags);

	if (MS::kSuccess != (status = 
		MGlobal::executeCommand(command, result, false, true))) {
		MStatus stringStat;
		MString msg = MStringResource::getString(kAnimCurveNotFound, stringStat);
		MGlobal::displayError(msg);
		return status;
	}

	if (result == 0 || MAnimCurveClipboard::theAPIClipboard().isEmpty()) {
		MString msg = MStringResource::getString(kAnimCurveNotFound, status);
		MGlobal::displayError(msg);
		return (MS::kFailure);
	}

	if (MS::kSuccess != (	status = 
							fWriter.writeClipboard(animFile, 
							MAnimCurveClipboard::theAPIClipboard(),
							nodeNames, verboseUnits))) {
		return (MS::kFailure);
	}

	return status;
}


MStatus initializePlugin(MObject obj)
{
	MStatus stat = MS::kFailure;
	MFnPlugin impPlugIn(obj, PLUGIN_COMPANY, "3.0", "Any");
	
	// This is done first, so the strings are available. 
	stat = impPlugIn.registerUIStrings(registerMStringResources, "animImportExportInitStrings");
	if (stat != MS::kSuccess)
	{
		stat.perror("registerUIStrings");
		return stat;
	}

	stat = impPlugIn.registerFileTranslator("animImport", "none",
											animImport::creator,
											(char *)animImportOptionScript,
											(char *)animImportDefaultOptions, 
											true);

	if (stat != MS::kSuccess) {
		return stat;
	}

	MFnPlugin expPlugIn(obj, PLUGIN_COMPANY, "3.0", "Any");
	stat = expPlugIn.registerFileTranslator("animExport", "",
										animExport::creator,
										(char *)animExportOptionScript,
										(char *)animExportDefaultOptions,
										true);

	return stat;
}

MStatus uninitializePlugin(MObject obj)
{
	MStatus stat = MS::kFailure;

	MFnPlugin impPlugIn(obj);
	stat = impPlugIn.deregisterFileTranslator("animImport");

	if (stat != MS::kSuccess) {
		return stat;
	}

	MFnPlugin expPlugIn(obj);
	stat = expPlugIn.deregisterFileTranslator("animExport");

	return stat;
}
